﻿Imports Co9320.NamaAlert.Model
Imports Co9320.NamaAlert.Engine
Imports System.Net
Imports System.ComponentModel
Imports System.Threading
Imports Co9320.NamaAlert.Contract
Imports Microsoft.WindowsAPICodePack
Imports Microsoft.WindowsAPICodePack.Taskbar
Imports Microsoft.WindowsAPICodePack.Shell

<ServiceModel.ServiceBehavior(InstanceContextMode:=ServiceModel.InstanceContextMode.Single)> _
Public Class MainForm
    Implements Contract.IApplicationService

    Private WithEvents LiveCommentServer As LiveCommentServer

    Private LivePrograms As New List(Of LiveProgram)
    Private LiveProgramsLockSlim As New System.Threading.ReaderWriterLockSlim

    Private StationIcons As New Dictionary(Of String, IconContext)
    Private StationIconsLockSlim As New System.Threading.ReaderWriterLockSlim

    Private QueuingIcons As New Queue(Of IconContext)
    Private QueuingIconsLockSlim As New System.Threading.ReaderWriterLockSlim
    Private QueuingIconsTimer As New Timer(AddressOf QueuingIconCallback, Nothing, -1, -1)


    Private AlertWindowSemaphore As New System.Threading.Semaphore(10, 10)

    Private Headers As New Dictionary(Of HeaderKind, HeaderContext)

    Private Const ApplicationId As String = "NamaAlertPortable"

    Private Enum HeaderKind
        None
        Alerted
        OnAir
        OffAir
    End Enum

    Private Class HeaderContext
        Private _Text As String
        Public Property Text() As String
            Get
                Return _Text
            End Get
            Set(ByVal value As String)
                _Text = value
            End Set
        End Property

        Private _RowCount As Integer
        Public Property RowCount() As Integer
            Get
                Return _RowCount
            End Get
            Set(ByVal value As Integer)
                _RowCount = value
            End Set
        End Property

        Private _IsExpanded As Boolean
        Public Property IsExpanded() As Boolean
            Get
                Return _IsExpanded
            End Get
            Set(ByVal value As Boolean)
                _IsExpanded = value
            End Set
        End Property
    End Class

#Region "CertificatePolicy Class"

    ' For Mono to access web site using ssl
    Private Class CertificatePolicy
        Implements ICertificatePolicy

        Public Function CheckValidationResult(ByVal srvPoint As System.Net.ServicePoint, ByVal certificate As System.Security.Cryptography.X509Certificates.X509Certificate, ByVal request As System.Net.WebRequest, ByVal certificateProblem As Integer) As Boolean Implements System.Net.ICertificatePolicy.CheckValidationResult
            Return True
        End Function
    End Class

#End Region

    Private Class IconContext
        Private _Id As String
        Public Property Id() As String
            Get
                Return _Id
            End Get
            Set(ByVal value As String)
                _Id = value
            End Set
        End Property

        Private _Uri As Uri
        Public Property Uri() As Uri
            Get
                Return _Uri
            End Get
            Set(ByVal value As Uri)
                _Uri = value
            End Set
        End Property

        Private _Icon As Bitmap
        Public Property Icon() As Bitmap
            Get
                Return _Icon
            End Get
            Set(ByVal value As Bitmap)
                _Icon = value
            End Set
        End Property

        Private _Thumbnail As Bitmap
        Public Property Thumbnail() As Bitmap
            Get
                Return _Thumbnail
            End Get
            Set(ByVal value As Bitmap)
                _Thumbnail = value
            End Set
        End Property

        Private _RowIndex As Integer
        Public Property RowIndex() As Integer
            Get
                Return _RowIndex
            End Get
            Set(ByVal value As Integer)
                _RowIndex = value
            End Set
        End Property

        Private _ColumnIndex As Integer
        Public Property ColumnIndex() As Integer
            Get
                Return _ColumnIndex
            End Get
            Set(ByVal value As Integer)
                _ColumnIndex = value
            End Set
        End Property

        'Private _IsProcessing As Boolean
        'Public Property IsProcessing() As Boolean
        '    Get
        '        Return _IsProcessing
        '    End Get
        '    Set(ByVal value As Boolean)
        '        _IsProcessing = value
        '    End Set
        'End Property
    End Class

#Region "Private properties"

    Private ReadOnly Property IsRunningOnMono() As Boolean
        Get
            Return Type.GetType("Mono.Runtime") IsNot Nothing
        End Get
    End Property

    Private _ApplicationPath As String
    Private ReadOnly Property ApplicationPath() As String
        Get
            If _ApplicationPath Is Nothing Then
                _ApplicationPath = System.Reflection.Assembly.GetEntryAssembly().Location
            End If
            Return _ApplicationPath
        End Get
    End Property

#End Region

    Public Sub New()
        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        ' Set our title if we were launched from the Taskbar

        Initialize()
    End Sub

    Private Sub Initialize()
        ' Network
        If IsRunningOnMono Then
            ' MEMO MONO doesn't implement ServerCertificateValidationCallback
            System.Net.ServicePointManager.CertificatePolicy = New CertificatePolicy
        End If

        ' NamaAlert API
        If Not IsRunningOnMono Then
            ApplicationServer.Open(Me)
        End If

        ' Live Comment Server
        LiveCommentServer = LiveCommentServer.CreateAnonymousSession("Co9320 NamaAlert(original) type 7 Portable")
        LiveCommentServer.EnableFiltering = False

        For Each id In LiveCommentServer.RegisteredStationIds
            LiveCommentServer.RegisterNotifiedStation(id)
        Next
        LiveCommentServer.Start()

        ' Header
        Headers.Add(HeaderKind.Alerted, New HeaderContext With {.Text = "アラート", .IsExpanded = True})
        Headers.Add(HeaderKind.OnAir, New HeaderContext With {.Text = "オンエア", .IsExpanded = True})
        Headers.Add(HeaderKind.OffAir, New HeaderContext With {.Text = "オフエア", .IsExpanded = True})

        LiveProgramsDataGridView.Rows.Add(Headers.Count)

        ' DataGridView
        Dim type = GetType(DataGridView)
        Dim propInfo = type.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic)
        propInfo.SetValue(LiveProgramsDataGridView, True, Nothing)

    End Sub

#Region "Jump List"

    Private Sub ResetJumpList()

        TaskbarManager.Instance.ApplicationId = ApplicationId

        Dim list = JumpList.CreateJumpList
        list.ClearAllUserTasks()

        Dim tasks = New List(Of JumpListLink)

        tasks.Add(New JumpListLink(ApplicationPath, "設定") With {.Arguments = "/jumplist:settings"})
        tasks.Add(New JumpListLink(ApplicationPath, "マイページ") With {.Arguments = "/jumplist:mypage"})

        'tasks.Add(New JumpListLink("http://live.nicovideo.jp/my", "マイページ") With { _
        '    .IconReference = New IconReference("", 0), _
        '    .Arguments = ""})

        list.AddUserTasks(tasks.ToArray)
        list.Refresh()
    End Sub

#End Region

    Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        'Dim uri = New Uri("http://icon.nimg.jp/community/co9320.jpg?1251599597")
        'Dim client = New WebClient
        'Dim stream = client.OpenRead(uri)
        'Dim bmp = New Bitmap(stream)
        'stream.Close()

        'Dim thumb = New Bitmap(24, 24)
        'Dim g = Graphics.FromImage(thumb)
        'g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High
        'g.DrawImage(bmp, 0, 0, 24, 24)
        'g.Dispose()

        'SmallImageList.Images.Add("co9320", bmp.GetThumbnailImage(24, 24, Nothing, IntPtr.Zero))
        'Dim item = New ListViewItem("Co9320", "co9320")
        'LiveProgramsListView.Items.Add(item)

        'SmallImageList.Images.Add("co9320a", thumb)
        'LiveProgramsListView.Items.Add(New ListViewItem("Co9320a", "co9320a"))


        'For i = 0 To 20
        '    Dim frm = New AlertForm(Nothing, AlertForm.AlertPosition.UpperRight)
        '    frm.Text = i.ToString
        '    frm.Show()
        'Next



    End Sub

    Private Sub MainForm_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        If IsRunningOnMono Then
            Exit Sub
        End If

        If Not TaskbarManager.IsPlatformSupported Then
            Exit Sub
        End If

        ResetJumpList()
    End Sub

    Private IsClosing As Boolean
    Private Sub MainForm_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        IsClosing = True
        Me.Hide()
        Try
            If LiveCommentServer IsNot Nothing AndAlso LiveCommentServer.IsStarted Then
                LiveCommentServer.Dispose()
            End If
        Catch ex As Exception
            ' Ignore
        End Try
    End Sub


#Region "LiveCommentServer"

    Private Sub LiveCommentServer_AuthenticationFailed(ByVal sender As Object, ByVal e As System.EventArgs) Handles LiveCommentServer.AuthenticationFailed
        Stop
    End Sub

    Private Sub LiveCommentServer_LiveProgramStarted(ByVal sender As Object, ByVal e As Co9320.NamaAlert.Model.LiveProgramEventArgs) Handles LiveCommentServer.LiveProgramStarted
        If IsClosing Then
            Exit Sub
        End If
        Debug.WriteLine("LiveCommentServer_LiveProgramStarted" & System.Threading.Thread.CurrentThread.ManagedThreadId.ToString)
        Try

            Static count As Integer
            If count > 10 Then
                Exit Sub
            End If

            LiveProgramsLockSlim.EnterWriteLock()
            LivePrograms.Add(DirectCast(e.LiveProgram.Clone, LiveProgram))
            LiveProgramsLockSlim.ExitWriteLock()

            If count < 3 Then
                Headers(HeaderKind.Alerted).RowCount += 1
                'LivePrograms(count).Title = count.ToString
            ElseIf count < 9 Then
                Headers(HeaderKind.OnAir).RowCount += 1
                'LivePrograms(count).Title = count.ToString
            Else
                Headers(HeaderKind.OffAir).RowCount += 1
                'LivePrograms(count).Title = count.ToString
            End If
            count += 1


            Me.Invoke(New Action(AddressOf AddLiveProgramsDataGridViewRow))
        Catch ex As Exception
            ' Ignore
#If DEBUG Then
            Stop
#End If
        End Try

    End Sub

    Private Sub AddLiveProgramsDataGridViewRow()

        Debug.Print("AddLiveProgramsDataGridViewRow Thread: {0}" & System.Threading.Thread.CurrentThread.ManagedThreadId)
        LiveProgramsDataGridView.Rows.Add()

        'Threading.ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(AddressOf ShowAlertWindow), program)
        ' http://icon.nimg.jp/community/s/co84791.jpg
        'AlertWindowSemaphore.WaitOne()
        'ShowAlertWindow(program)

    End Sub

#End Region


    Private Sub ShowAlertWindow(ByVal program As Object)

        Dim form = New AlertForm(DirectCast(program, LiveProgram), AlertForm.AlertPosition.LowerRight)
        form.Show()
        'AlertWindowSemaphore.Release()

    End Sub

#Region "DataGridView"

    Private Sub LiveProgramsDataGridView_CellFormatting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellFormattingEventArgs) Handles LiveProgramsDataGridView.CellFormatting

        If Not TypeOf LiveProgramsDataGridView.Columns(e.ColumnIndex) Is DataGridViewLinkColumn Then
            Exit Sub
        End If

        ' LinkColumn Color 設定
        Dim cell = DirectCast(LiveProgramsDataGridView.Rows(e.RowIndex).Cells(e.ColumnIndex), DataGridViewLinkCell)
        If LiveProgramsDataGridView.Rows(e.RowIndex).Selected Then
            cell.LinkColor = Color.White
        Else
            cell.LinkColor = DirectCast(LiveProgramsDataGridView.Columns(e.ColumnIndex), DataGridViewLinkColumn).LinkColor
        End If
    End Sub

    Private Sub LiveProgramsDataGridView_CellContentClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles LiveProgramsDataGridView.CellContentClick
        Debug.Print("LiveProgramsDataGridView_CellContentClick Thread: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId)

        If e.RowIndex = -1 Then
            Exit Sub
        End If

        Dim program = GetSelectedLiveProgram(e.RowIndex)
        If program Is Nothing Then
            Exit Sub
        End If

        Select Case e.ColumnIndex
            Case 1
                If ViewerPath <> "" Then
                    Process.Start(ViewerPath, String.Format("lv{0} /singleinstance", program.Id))
                Else
                    Process.Start(program.Uri.ToString)
                End If

            Case 2

        End Select

    End Sub

    Private Function IsHeaderRow(ByVal rowIndex As Integer, Optional ByRef context As HeaderContext = Nothing) As Boolean

        Dim index As Integer
        Dim offset As Integer
        For Each h In Headers.Values
            offset += 1

            If rowIndex = offset - 1 Then
                context = h
                Return True
            End If

            If Not h.IsExpanded Then
                offset += h.RowCount
                Continue For
            End If

            If offset <= rowIndex AndAlso rowIndex < offset + h.RowCount Then
                index += rowIndex - offset
                Exit For
            End If

            offset += h.RowCount
            index += h.RowCount
        Next

        Return False

    End Function

    Private Sub LiveProgramsDataGridView_RowPrePaint(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowPrePaintEventArgs) Handles LiveProgramsDataGridView.RowPrePaint

        If (e.PaintParts And DataGridViewPaintParts.Background) = 0 AndAlso _
           (e.PaintParts And DataGridViewPaintParts.ContentForeground) = 0 Then
            Exit Sub
        End If

        Dim context As HeaderContext = Nothing
        If Not IsHeaderRow(e.RowIndex, context) Then
            Exit Sub
        End If

        ' 背景
        Dim backColor As Color
        If (e.PaintParts And DataGridViewPaintParts.SelectionBackground) > 0 AndAlso _
            (e.State And DataGridViewElementStates.Selected) > 0 Then
            backColor = Color.FromArgb(235, 235, 235)

        Else
            backColor = Color.White
        End If

        If (e.PaintParts And DataGridViewPaintParts.Background) > 0 Then

            Dim width As Integer
            If IsRunningOnMono Then
                width = e.RowBounds.Width
            Else
                width = LiveProgramsDataGridView.Columns.GetColumnsWidth(DataGridViewElementStates.Visible)
            End If
            Using b = New SolidBrush(backColor)
                e.Graphics.FillRectangle(b, New RectangleF( _
                                                e.RowBounds.X, _
                                                e.RowBounds.Y, _
                                                width, _
                                                e.RowBounds.Height))
            End Using
        End If


        ' [+]画像, テキスト
        Dim image As Bitmap
        If context.IsExpanded Then
            image = My.Resources.ToggleImage
        Else
            image = My.Resources.ExpandToggleImage
        End If
        Const offsetY As Integer = 1
        If (e.PaintParts And DataGridViewPaintParts.ContentForeground) > 0 Then
            e.Graphics.DrawImage(image, _
                                 New RectangleF(e.RowBounds.X + 5, _
                                                e.RowBounds.Y + (e.RowBounds.Height - image.Height) \ 2 + offsetY, _
                                                image.Width, _
                                                image.Height))

            Using b = New SolidBrush(LiveProgramsDataGridView.ForeColor)
                Dim text = String.Format("{0}({1})", context.Text, context.RowCount)
                Dim size = e.Graphics.MeasureString(text, LiveProgramsDataGridView.RowHeadersDefaultCellStyle.Font, e.RowBounds.Size, StringFormat.GenericDefault)
                e.Graphics.DrawString(text, _
                                      LiveProgramsDataGridView.RowHeadersDefaultCellStyle.Font, _
                                      b, _
                                      e.RowBounds.X + 20, _
                                      e.RowBounds.Y + (e.RowBounds.Height - Convert.ToInt32(size.Height)) \ 2)
            End Using
        End If

        e.PaintParts = e.PaintParts And Not (DataGridViewPaintParts.Background Or DataGridViewPaintParts.ContentForeground Or DataGridViewPaintParts.SelectionBackground)


    End Sub

    Private Sub LiveProgramsDataGridView_RowPostPaint(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewRowPostPaintEventArgs) Handles LiveProgramsDataGridView.RowPostPaint

        Dim context As HeaderContext = Nothing

        If Not IsHeaderRow(e.RowIndex, context) Then
            Exit Sub
        End If

        Using pen = New Pen(Color.LightGray)
            e.Graphics.DrawLine(pen, e.RowBounds.X, e.RowBounds.Y + e.RowBounds.Height - 1, e.RowBounds.X + e.RowBounds.Width, e.RowBounds.Y + e.RowBounds.Height - 1)
        End Using


    End Sub

    Private Sub LiveProgramsDataGridView_ColumnWidthChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs) Handles LiveProgramsDataGridView.ColumnWidthChanged
        ' ヘッダー用 再描画
        LiveProgramsDataGridView.Invalidate()
    End Sub

    Private Function GetSelectedLiveProgram(ByVal rowIndex As Integer) As LiveProgram

        Dim index As Integer
        Dim offset As Integer
        For Each h In Headers.Values
            offset += 1


            If rowIndex = offset - 1 Then
                Return Nothing
            End If

            If Not h.IsExpanded Then
                offset += h.RowCount
                Continue For
            End If

            If offset <= rowIndex AndAlso rowIndex < offset + h.RowCount Then

                index += rowIndex - offset
                Exit For
            End If

            offset += h.RowCount
            index += h.RowCount
        Next

        LiveProgramsLockSlim.EnterReadLock()
        Dim program = DirectCast(LivePrograms(index).Clone, LiveProgram)
        LiveProgramsLockSlim.ExitReadLock()

        Return program
    End Function

    Private Sub LiveProgramsDataGridView_CellValueNeeded(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValueEventArgs) Handles LiveProgramsDataGridView.CellValueNeeded
        Debug.Print("LiveProgramsDataGridView_CellValueNeeded Thread: {0}, Row: {1}, Col: {2}", System.Threading.Thread.CurrentThread.ManagedThreadId, e.RowIndex, e.ColumnIndex)

        Dim program = GetSelectedLiveProgram(e.RowIndex)
        If program Is Nothing Then
            If e.ColumnIndex = 1 Then
                ' for MONO
                e.Value = "|"
            End If
            Exit Sub
        End If

        Select Case e.ColumnIndex
            Case 0
                ' アイコン
                StationIconsLockSlim.EnterUpgradeableReadLock()
                If StationIcons.ContainsKey(program.Station.Id) Then
                    e.Value = StationIcons(program.Station.Id).Thumbnail
                Else
                    StationIconsLockSlim.EnterWriteLock()
                    StationIcons.Add(program.Station.Id, New IconContext)
                    StationIconsLockSlim.ExitWriteLock()

                    Dim iconContext = New IconContext With {.Id = program.Station.Id, _
                                                            .Uri = program.Station.IconUri, _
                                                            .RowIndex = e.RowIndex, _
                                                            .ColumnIndex = e.ColumnIndex}
                    QueuingIconsLockSlim.EnterWriteLock()
                    QueuingIcons.Enqueue(iconContext)
                    QueuingIconsLockSlim.ExitWriteLock()
                    QueuingIconsTimer.Change(0, -1)
                End If
                StationIconsLockSlim.ExitUpgradeableReadLock()

            Case 1
                ' タイトル
                e.Value = program.Title

            Case 2
                ' 放送局
                e.Value = program.Station.Name
        End Select



    End Sub

    Private Sub LiveProgramsDataGridView_ColumnHeaderMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles LiveProgramsDataGridView.ColumnHeaderMouseClick

    End Sub

#End Region

    Private Sub QueuingIconCallback(ByVal state As Object)
        Debug.Print("QueuingIconCallback Thread: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId)
        QueuingIconsTimer.Change(-1, -1)

        Dim context As IconContext = Nothing
        Try
            QueuingIconsLockSlim.EnterWriteLock()
            If QueuingIcons.Count = 0 Then
                Exit Sub
            End If
            context = QueuingIcons.Dequeue
        Catch ex As Exception
            ' Ignore
        Finally
            QueuingIconsLockSlim.ExitWriteLock()
        End Try

        Try
            DownloadIcon(context)
            ResizeIcon(context)

            StationIconsLockSlim.EnterWriteLock()
            If Not StationIcons.ContainsKey(context.Id) Then
                StationIcons.Add(context.Id, context)
            Else
                StationIcons(context.Id) = context
            End If
            StationIconsLockSlim.ExitWriteLock()

            Me.Invoke(New Action(Of IconContext)(AddressOf ShowStationThumbnailIcon), New Object() {context})

        Catch webEx As WebException
            Exit Sub
        Catch ex As Exception
            Exit Sub

        Finally
            QueuingIconsLockSlim.EnterReadLock()
            If QueuingIcons.Count > 0 Then
                QueuingIconsTimer.Change(0, -1)
            End If
            QueuingIconsLockSlim.ExitReadLock()
        End Try

    End Sub

    Private Sub DownloadIcon(ByVal context As IconContext)
        Dim bmp As Bitmap
        Dim client = New WebClient
        Using stream = client.OpenRead(context.Uri)
            bmp = New Bitmap(stream)
            context.Icon = bmp
        End Using
    End Sub

    Private Sub ResizeIcon(ByVal context As IconContext)
        Dim thumb = New Bitmap(16, 16)
        Using g = Graphics.FromImage(thumb)
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High
            g.DrawImage(context.Icon, 0, 0, 16, 16)
            g.Dispose()
        End Using
        context.Thumbnail = thumb
    End Sub

    Private Sub ShowStationThumbnailIcon(ByVal context As IconContext)
        Debug.Print("ShowStationThumbnailIcon Thread: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId)

        LiveProgramsDataGridView.UpdateCellValue(context.ColumnIndex, context.RowIndex)
    End Sub


    Private Sub ToolStripButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton1.Click

        Dim fd = New FontDialog
        fd.Font = LiveProgramsDataGridView.DefaultCellStyle.Font

        If fd.ShowDialog = Windows.Forms.DialogResult.OK Then
            LiveProgramsDataGridView.RowHeadersDefaultCellStyle.Font = fd.Font
            LiveProgramsDataGridView.DefaultCellStyle.Font = fd.Font
            LiveProgramsDataGridView.AutoResizeRows()
        End If
        fd.Dispose()
    End Sub

#Region "Application API"

    Public Sub AddFavorite(ByVal stationId As String) Implements Contract.IApplicationService.AddFavorite
        'MessageBox.Show(stationId)
    End Sub

    Public Function IsFavorite(ByVal stationId As String) As Boolean Implements Contract.IApplicationService.IsFavorite
        Return True
    End Function

    Private ViewerPath As String

    Public Sub Register(ByVal applicationId As System.Guid, ByVal path As String) Implements Contract.IApplicationService.Register
        If applicationId.ToString.ToUpperInvariant = "1C2F8706-9970-4C7A-884E-0BEB9F6CB870" Then
            ViewerPath = path
        End If
    End Sub

#End Region






    Public Function GetAlertedLivePrograms() As System.Collections.Generic.IList(Of Model.LiveProgram) Implements Contract.IApplicationService.GetAlertedLivePrograms

    End Function

    Public Function GetAllLivePrograms() As System.Collections.Generic.IList(Of Model.LiveProgram) Implements Contract.IApplicationService.GetAllLivePrograms

    End Function

    Public Function GetFavoriteStations() As System.Collections.Generic.IList(Of Model.Station) Implements Contract.IApplicationService.GetFavoriteStations

    End Function

    Public Function GetVoteCountCombination(ByVal percentCombination As System.Collections.Generic.IList(Of Integer)) As System.Collections.Generic.IList(Of Integer) Implements Contract.IApplicationService.GetVoteCountCombination

    End Function

    Public Sub StartTimer(ByVal id As String, ByVal time As System.TimeSpan) Implements Contract.IApplicationService.StartTimer

    End Sub

    Public Sub StopTimer(ByVal id As String) Implements Contract.IApplicationService.StopTimer

    End Sub
End Class
